from datetime import date
from django.db import transaction
from analyzer.statistics.models import DailyProjectStats, IndustryConcentration, CompanyWinStats, AmountBucketStats
from analyzer.statistics.services import _get_region, _get_city, _get_industry
from deal.models import Project, WinningResultAnnouncement, BidOpeningRecord, BidOpeningDetail


def compute_daily_project_stats(begin: date, end: date):
    with transaction.atomic():
        qs = Project.objects.filter(created_at__date__gte=begin, created_at__date__lte=end)
        rows = list(qs.values('created_at', 'project_type', 'extra_data'))
        agg = {}
        for r in rows:
            day = r['created_at'].date()
            region = _get_region(r['extra_data'])
            city = _get_city(r['extra_data'])
            industry = _get_industry(r['project_type'], r['extra_data'])
            key = (day, region, city, r['project_type'] or '', industry)
            agg[key] = agg.get(key, 0) + 1
        for (day, region, city, ptype, ind), count in agg.items():
            DailyProjectStats.objects.update_or_create(
                day=day, region=region, city=city, project_type=ptype, industry=ind,
                defaults={'project_count': count}
            )


def compute_industry_concentration(period: str, begin: date, end: date):
    with transaction.atomic():
        qs = WinningResultAnnouncement.objects.select_related('project').filter(announcement_date__date__gte=begin, announcement_date__date__lte=end)
        rows = list(qs.values('project__project_type', 'project__extra_data', 'winning_company_id', 'winning_amount'))
        groups = {}
        for r in rows:
            region = _get_region(r['project__extra_data'])
            city = _get_city(r['project__extra_data'])
            industry = _get_industry(r['project__project_type'], r['project__extra_data'])
            key = (region, city, industry)
            groups.setdefault(key, {})
            cid = r['winning_company_id']
            amt = float(r['winning_amount'] or 0)
            groups[key][cid] = groups[key].get(cid, 0.0) + amt
        for (region, city, industry), m in groups.items():
            total = sum(m.values())
            if total <= 0:
                hhi = 0.0
                top_share = 0.0
            else:
                shares = [v / total for v in m.values()]
                hhi = sum([(s * 100) ** 2 for s in shares])
                top_share = max(shares) if shares else 0.0
            IndustryConcentration.objects.update_or_create(
                period=period, region=region, city=city, industry=industry,
                defaults={'hhi': hhi, 'top_share': top_share}
            )


def compute_company_win_stats(period: str, begin: date, end: date):
    with transaction.atomic():
        br = BidOpeningRecord.objects.filter(opening_time__date__gte=begin, opening_time__date__lte=end)
        bod = BidOpeningDetail.objects.filter(bid_opening_record__in=br).select_related('bid_opening_record__project')
        bid_rows = list(bod.values('company_id', 'bid_opening_record__project__project_type', 'bid_opening_record__project__extra_data'))
        bid_map = {}
        for r in bid_rows:
            region = _get_region(r['bid_opening_record__project__extra_data'])
            city = _get_city(r['bid_opening_record__project__extra_data'])
            industry = _get_industry(r['bid_opening_record__project__project_type'], r['bid_opening_record__project__extra_data'])
            key = (region, city, industry, r['company_id'])
            bid_map[key] = bid_map.get(key, 0) + 1
        wra = WinningResultAnnouncement.objects.filter(announcement_date__date__gte=begin, announcement_date__date__lte=end).select_related('project')
        win_rows = list(wra.values('winning_company_id', 'project__project_type', 'project__extra_data'))
        win_map = {}
        for r in win_rows:
            region = _get_region(r['project__extra_data'])
            city = _get_city(r['project__extra_data'])
            industry = _get_industry(r['project__project_type'], r['project__extra_data'])
            key = (region, city, industry, r['winning_company_id'])
            win_map[key] = win_map.get(key, 0) + 1
        keys = set(bid_map.keys()) | set(win_map.keys())
        for (region, city, industry, cid) in keys:
            bid_count = bid_map.get((region, city, industry, cid), 0)
            win_count = win_map.get((region, city, industry, cid), 0)
            win_rate = (float(win_count) / float(bid_count)) if bid_count > 0 else 0.0
            CompanyWinStats.objects.update_or_create(
                period=period, region=region, city=city, industry=industry, company_id=cid,
                defaults={'bid_count': bid_count, 'win_count': win_count, 'win_rate': win_rate}
            )


def compute_amount_bucket_stats(period: str, begin: date, end: date):
    buckets = [
        (0, 500000, '0-50万'),
        (500000, 2000000, '50-200万'),
        (2000000, 10000000, '200-1000万'),
        (10000000, None, '1000万以上'),
    ]
    def push(source: str, rows):
        from collections import defaultdict
        acc = defaultdict(lambda: defaultdict(lambda: {'count': 0, 'total': 0.0}))
        for r in rows:
            amt = float(r['amount'] or 0)
            region = r['region']
            city = r['city']
            industry = r['industry']
            label = None
            for low, high, name in buckets:
                if high is None and amt >= low:
                    label = name
                    break
                if amt >= low and amt < high:
                    label = name
                    break
            if label is None:
                continue
            acc[(region, city, industry)][label]['count'] += 1
            acc[(region, city, industry)][label]['total'] += amt
        for (region, city, industry), m in acc.items():
            for label, v in m.items():
                AmountBucketStats.objects.update_or_create(
                    period=period, source=source, region=region, city=city, industry=industry, bucket=label,
                    defaults={'count': v['count'], 'total': v['total']}
                )
    # budget
    pr = Project.objects.filter(created_at__date__gte=begin, created_at__date__lte=end).values('budget_amount', 'project_type', 'extra_data')
    budget_rows = [{'amount': p['budget_amount'], 'region': _get_region(p['extra_data']), 'city': _get_city(p['extra_data']), 'industry': _get_industry(p['project_type'], p['extra_data'])} for p in pr]
    push('budget', budget_rows)
    # winning
    wr = WinningResultAnnouncement.objects.filter(announcement_date__date__gte=begin, announcement_date__date__lte=end).select_related('project').values('winning_amount', 'project__project_type', 'project__extra_data')
    winning_rows = [{'amount': w['winning_amount'], 'region': _get_region(w['project__extra_data']), 'city': _get_city(w['project__extra_data']), 'industry': _get_industry(w['project__project_type'], w['project__extra_data'])} for w in wr]
    push('winning', winning_rows)
